#include "GateHandler.h"

#include <ace/Log_Msg.h>
#include <ace/config.h>

#include <google/protobuf/io/coded_stream.h>

#include <InputBuffer.h>

#include "Client.h"
#include "service.pb.h"

GateWayHandler*
GateWayHandler::instance()
{
	static GateWayHandler handler;
	return(&handler);
}

GateWayHandler::GateWayHandler()
: RpcChannel(BUFFER_SIZE)
{
}

GateWayHandler::~GateWayHandler()
{
}


//int 
//GateWayHandler::open(void * )
//{
//	return 0;
//}

int
GateWayHandler::handle_input(ACE_HANDLE fd )
{
	RpcChannel::handle_input(fd);

	::google::protobuf::Descriptor const *head_desc = 
		::google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("Head");
	
	
	while(true)
	{
		if(0==recv_buffer_->length())
			return 0;
		if(sizeof(short)*4 > recv_buffer_->length())
			return 0;

		short guid = 0;
		ACE_OS::memcpy(&guid,recv_buffer_->rd_ptr(),sizeof(guid));
		recv_buffer_->rd_ptr(sizeof(guid));
		short length = 0;
		ACE_OS::memcpy(&length,recv_buffer_->rd_ptr(),sizeof(length));
		recv_buffer_->rd_ptr(sizeof(length));
		short service = 0;
		ACE_OS::memcpy(&service,recv_buffer_->rd_ptr(),sizeof(service));
		recv_buffer_->rd_ptr(sizeof(service));
		short method = 0;
		ACE_OS::memcpy(&method,recv_buffer_->rd_ptr(),sizeof(method));
		recv_buffer_->rd_ptr(sizeof(method));
		
		if(length >recv_buffer_->length())
			return 0;

		ACE_DEBUG((LM_DEBUG,ACE_TEXT ("%d , %d , %d , %d\n"),length,service,method,guid)); 

		::google::protobuf::ServiceDescriptor const * service_desc = CSProtocol::descriptor();
		::google::protobuf::MethodDescriptor const *method_desc = service_desc->method(method);
		::google::protobuf::Message * msg =  
			::google::protobuf::MessageFactory::generated_factory()->GetPrototype(method_desc->input_type())->New();

		::google::protobuf::io::CodedInputStream input((unsigned char*) recv_buffer_->rd_ptr() ,length);

		msg->ParseFromCodedStream(&input);

		recv_buffer_->rd_ptr(length);

		static Client client;

		client.CallMethod(method_desc,NULL,msg,NULL,NULL);

		delete msg;
		recv_buffer_->crunch();

	}
	

	//}
	return 0;
}

//int
//GateWayHandler::handle_output(ACE_HANDLE fd )
//{
//	return 0;
//}

int
GateWayHandler::handle_close(ACE_HANDLE,ACE_Reactor_Mask)
{
	ACE_DEBUG((LM_INFO,ACE_TEXT("gateway disconnected\n")));
	ACE_ASSERT(0);
	return 0;
}

GateWayAcceptor::GateWayAcceptor()
: _acceptor()
{
	
}

GateWayAcceptor::~GateWayAcceptor(){}

ACE_HANDLE 
GateWayAcceptor::get_handle() const
{
	return _acceptor.get_handle();
}

int 
GateWayAcceptor::open(ACE_INET_Addr const &addr)
{
	ACE_TRACE("AcceptHandler::open(void)");
	if (-1==_acceptor.open(addr))
	{
		ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to open ")
		ACE_TEXT ("listening socket. (errno = %i: %m)\n"), ACE_ERRNO_GET), -1);
	}
	if (-1==ACE_Reactor::instance()->register_handler(this,ACE_Event_Handler::ACCEPT_MASK))
	{
			ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to register accept ")
				ACE_TEXT ("handler. (errno = %i: %m)\n"), ACE_ERRNO_GET));

			// don't leave the acceptor open
			if (-1==_acceptor.close())
			{
				ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close the socket ")
				ACE_TEXT ("after previous error. (errno = %i: %m)\n"),
				ACE_ERRNO_GET));
			}
			return -1;
	}

	return 0;
}

int
GateWayAcceptor::handle_input(ACE_HANDLE fd )
{
	ACE_TRACE("AcceptHandler::handle_input(ACE_HANDLE)");

	ACE_INET_Addr addr;
	
	if (-1==_acceptor.accept(GateWayHandler::instance()->peer(), &addr))
	{
		ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to accept ")
		ACE_TEXT ("client connection. (errno = %i: %m)\n"), ACE_ERRNO_GET), -1);
	}
	GateWayHandler::instance()->reactor(this->reactor());

	ACE_DEBUG((LM_INFO,ACE_TEXT("gate way connected address[%s:%d] \n"),addr.get_host_addr(),addr.get_port_number()));

	return GateWayHandler::instance()->open(NULL);
}

int 
GateWayAcceptor::handle_close(ACE_HANDLE handle, ACE_Reactor_Mask close_mask)
{
	_acceptor.close();
	delete this;
	return 0;
}
